#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string>
#include <cstdio>

const int defaultid = -1;
const int gsize = 4096;
const std::string pathname = ".";
const int projid = 0x66;
const int gmode = 0666;

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    }                       \
    while(0)

#define CREATER "creater"
#define USER "user"

class Shm{
private:
    // 创建共享内存的辅助函数，根据传入的标志位创建共享内存
    void CreateHelper(int flag) {
        printf("key: 0c%x\n", _key);
        _shmid = shmget(_key, _size, flag);
        if(_shmid < 0) {
            ERR_EXIT("shmget");
        }
        printf("shmid: %d\n", _shmid);
    }
    // 创建共享内存 -- 使用 IPC_CREAT | IPC_EXCL | gmode 标志位，确保创建的是一个全新的共享内存
    void Create() { CreateHelper(IPC_CREAT | IPC_EXCL | 0666); }

    void Get() { CreateHelper(IPC_CREAT); }

    void Attach() {
        _start_memo = shmat(_shmid, nullptr, 0);
        if((long long)_start_memo < 0) {
            ERR_EXIT("shmat");
        }
        printf("attach success!\n");
    }

    // 将当前进程的地址空间与共享内存分离
    void Detach()
    {
        int n = shmdt(_start_mem); // 调用 shmdt 函数
        if (n == 0)
        {
            printf("detach success\n"); // 如果分离成功，打印信息
        }
    }


    // 销毁共享内存
    void Destroy()
    {
        // if (_shmid == gdefaultid)
        //     return;
        Detach();                 // 先分离共享内存
        if (_usertype == CREATER) // 如果是创建者类型
        {
            int n = shmctl(_shmid, IPC_RMID, nullptr); // 调用 shmctl 函数删除共享内存
            if (n > 0)
            {
                printf("shmctl delete shm: %d success!\n", _shmid); // 如果删除成功，打印信息
            }
            else
            {
                ERR_EXIT("shmctl"); // 如果删除失败，调用 ERR_EXIT 宏
            }
        }
    }

public:
    Shm(const std::string& pathname, int projid, const std::string& usertype)
    :_shmid(defaultid)
    ,_size(gsize)
    ,_start_memo(nullptr)
    ,_usertype(usertype)
    {
        _key = ftok(pathname.c_str(), projid);
        if(_key < 0) {
            ERR_EXIT("ftok");
        }
        if(_usertype == CREATER) Create();
        else if(_usertype == USER) Get();
        else {}
        Attach();
    }
    
    ~Shm() {
        std::cout << _usertype << std::endl;
        if(_usertype == CREATER) Destroy();
    }

    // 获取共享内存的虚拟地址
    void* VirtualAddr() const {
        printf("VirtualAddr: %p\n", _start_memo);
        return _start_memo;
    }

    // 获取共享内存的大小
    int Size() const {
        return _size;
    }
private:
    int _shmid;
    int _size;
    key_t _key;
    void* _start_memo;
    std::string _usertype;
};